<template>
  <XcDrawer ref="drawer" :title="$t('page.app.watch.title')+' - '+path" :width="960" :closable="true" :closeconfirm="true" @onDrawerClose="cancelWatch">
    <div v-if="!downflag">
      <StatusBadge class="mr-5" :content="$t('page.app.watch.loading')" badge="processing" :statuschangable="false"></StatusBadge>
      {{$t('page.app.watch.loading')}}
      <Time class="ml-5" v-if="timer" :time="timerStart" :interval="1" />
      <Divider />
    </div>
    <Tree :data="lists" :render="listsRender"></Tree>
  </XcDrawer>
</template>
<script>
  import XcDrawer from '@/components/xcdrawer'
  import StatusBadge from '@/components/statusbadge'
  export default {
    name: "comp-nodewatch",
    components: { XcDrawer, StatusBadge },
    data() {
      return {
        appname: '',
        nodeId: '',
        path: '',
        traceId: '',
        lists: [],
        canceld: false,
        syncs: 0,
        downflag: false,
        timer: false,
        timerStart: '',
      }
    },
    computed: {
      maxWidth() {
        return parseInt(this.width * 2 / 3) + 'px'
      }
    },
    destroyed() {
      this.cancelWatch()
    },
    methods: {
      show(appname, nodeId, path) {
        this.appname = appname
        this.nodeId = nodeId
        this.path = path
        this.traceId = ''
        this.lists = []
        this.syncs = 0
        this.canceld = false
        this.downflag = false
        this.timer = false
        this.$refs.drawer.show()
        // ===================================
        this.startWatch()
      },
      startWatch() {
        if (this.canceld) return
        if (this.syncs > 10) return
        setTimeout(() => {
          this.$md.dispatch('app_node_watch_trace', {
            path: this.path,
            data: {
              appname: this.appname,
              nodeId: this.nodeId,
            }
          }).then(data => {
            if (this.canceld) return
            if (data.retCode === 1) {
              if (data.data) {
                this.traceId = data.data
                this.syncs = 0
                this.watchSync()
              } else {
                this.syncs++
                this.startWatch()
              }
            }
          })
        }, Math.min(10000, (this.syncs + 1) * 1000))
      },
      watchSync() {
        if (this.canceld) return
        if (this.syncs > 10) return
        setTimeout(() => {
          this.$md.dispatch('app_node_watch_sync', {
            data: this.traceId
          }).then(data => {
            if (this.canceld) return
            if (data.retCode === 1) {
              let lists = data.data || []
              if (lists.length > 0 && !this.timer) {
                this.timerStart = new Date()
                this.timer = true
              }
              this.lists = this.resolveData(lists)
              this.syncs++
              if (!this.downflag) this.watchSync()
            }
          })
        }, Math.min(30000, this.syncs === 0 ? 1000 : this.syncs * 5000))
      },
      cancelWatch() {
        this.canceld = true
        this.$md.dispatch('app_node_watch_cancel', { data: this.traceId })
      },
      resolveData(watches) {
        if (this.canceld) return
        let total = watches.length
        if (total === 0) return
        let watch
        for (let i in watches) {
          watch = watches[i]
          watch.order = watch.inited ? watch.index : (total - watch.index - 1)
          watch.icon = watch.inited ? 'md-arrow-round-down' : 'md-arrow-round-up'
        }
        watches.sort((w1, w2) => { return w1.order - w2.order })
        // ============================================================
        // 看看是不是结束了
        let first = watches[0]
        let last = watches[total - 1]
        if ((first.node.service === last.node.service) && (first.path === last.path) && (first.inited === true) && (last.inited === false)) this.downflag = true
        // =================================================================
        let listsmap = {} // tree数据
        let treewatch
        let servicewatch
        let servicepath
        let servicepathmap = {} // 服务路径数据，用于定位具体的记录
        let lastwatch
        for (let i in watches) {
          watch = watches[i]
          // 服务路径存储起来
          servicepath = watch.node.service + '-' + watch.path
          // 发起请求的那条记录
          servicewatch = servicepathmap[servicepath]
          if (servicewatch) {
            // 同个请求的返回，将返回信息附加到原始请求信息中
            servicewatch.cost = watch.watchTime ? (watch.watchTime - servicewatch.cost) : '-'
            servicewatch.target = watch.target
            // 有后续请求，但请求目标不存在，说明没有正常收到返回，则补偿记录目标地址
            if (!servicewatch.target && servicewatch.children.length > 0 && servicewatch.node !== servicewatch.children[0].node) {
              servicewatch.target = servicewatch.children[0].node
            }
            // 根据处理结果设置颜色值
            servicewatch.color = watch.success ? 'green' : 'red'
            // 如果处理成功或者有后续请求，则说明已经成功发送出去了
            if (watch.success || servicewatch.children.length > 0) servicewatch.icon = watch.icon
            // 结束时间
            servicewatch.endTime = watch.watchTime ? this.$util.to_mmssSSS(watch.watchTime) : 'NAN'
            // 异常信息
            servicewatch.error = watch.error
            // 没有后续，说明没有找到实例
            servicewatch.e404 = servicewatch.children.length === 0
          } else {
            // 包装tree数据
            treewatch = {
              expand: true,
              service: watch.node.service,
              path: watch.path,
              icon: watch.icon,
              color: '#c5c8ce',
              children: [],
              cost: watch.watchTime || 0,
              startTime: this.$util.to_mmssSSS(watch.watchTime),
              error: watch.error,
              node: watch.node.host + ':' + watch.node.port
            }
            // 用host替代已知的实例地址
            if (treewatch.service != this.appname && treewatch.path.indexOf('http://' + treewatch.service + '/') === 0) {
              treewatch.path = treewatch.path.replace(treewatch.service, treewatch.node)
            }
            servicepathmap[servicepath] = treewatch
          }
          // map里已经存在
          if (watch.inited) {
            let listwatch = listsmap[watch.path]
            if (listwatch) {
              if (listwatch.service === treewatch.service) treewatch.service = '-'
              listwatch.children.push(treewatch)
              // 对于内部的请求，有后续请求，但请求目标不存在，则补偿记录目标地址
              if (!listwatch.target && listwatch.service === '-') listwatch.target = treewatch.node
            } else {
              listsmap[watch.path] = treewatch
              if (lastwatch) {
                if (lastwatch.service === treewatch.service) treewatch.service = '-'
                lastwatch.children.push(treewatch)
                // 对于内部的请求，有后续请求，但请求目标不存在，则补偿记录目标地址
                if (!lastwatch.target && lastwatch.service === '-') lastwatch.target = treewatch.node
              }
            }
            lastwatch = treewatch
          }
        }
        return [listsmap[watches[0].path]]
      },
      listsRender(h, { root, node, data }) {
        let doms = [
          h('span', [
            h('Icon', { style: { marginRight: '5px' }, props: { type: data.icon, color: data.color }, }),
            h('span', { style: { marginRight: '5px', fontWeight: 'bold' } }, '[' + data.service + '] '),
            h('span', { style: { fontWeight: 'bold' } }, (data.cost > 6000000 ? 'NAN' : data.cost)),
            h('span', 'ms (' + data.startTime + ' ~ ' + (data.endTime || 'NAN') + ')'),
          ]),
          h('span',
            { style: { display: 'block', width: this.maxWidth, paddingLeft: '20px' } },
            [
              data.path,
              h('span', { style: { paddingLeft: '10px', color: (data.e404 ? '#ed4014' : '#19be6b'), fontWeight: 'bold' } }, data.target ? ('> ' + data.target) : ''),
            ]),
        ]
        if (data.error) {
          doms.push(
            h('Tooltip',
              {
                style: { display: 'block', width: this.maxWidth, paddingLeft: '20px', color: '#ed4014', overflow: 'hidden', textOverflow: 'ellipsis' },
                props: { content: data.error, maxWidth: this.maxWidth, theme: 'light' }
              },
              data.error.length > 56 ? (data.error.substr(0, 56) + '...') : data.error)
          )
        }
        return h('span', { style: { display: 'block', width: this.maxWidth } }, doms)
      }
    }
  }
</script>
